<?php

namespace App\Library\Payment\Wechat;

use App\Helpers\LogHelper;
use Exception;
use Throwable;
use WeChatPay\Crypto\AesGcm;

/**
 *
 */
class WechatPayApi extends BaseWechatApi
{
    const KEY_LENGTH_BYTE      = 32;
    const AUTH_TAG_LENGTH_BYTE = 16;

    /**
     * app
     *
     * @param string $subject 标题
     * @param string $orderNo 订单号
     * @param int $orderPrice 订单金额
     * @param array $batchOptional 额外参数
     * @return array
     */
    public function appPay(string $subject, string $orderNo, int $orderPrice, array $batchOptional): array
    {
        $requestData = [
            'mchid' => self::$mchId,
            'out_trade_no' => $orderNo,
            'appid' => self::$appId,
            'description' => $subject,
            'notify_url' => self::$notifyPath,
            'amount' => [
                'total' => $orderPrice,
                'currency' => 'CNY',
            ],
        ];

        $this->setBatchOptional($requestData, $batchOptional);

        // 请求参数
        LogHelper::info('payment', ['requestData' => $requestData]);

        // 发送请求
        $response = $this->getInstance()->chain(self::PAY_ORDER_APP)->post(['json' => $requestData]);
        $result = json_decode($response->getBody()->getContents(), true);
        LogHelper::info('payment', ['response' => $response, 'result' => $result]);
        // 响应参数
        return $this->getAppPayParams($result['prepay_id']);
    }

    /**
     * 小程序支付
     * @return void
     */
    public function appletPay(string $subject, string $orderNo, int $orderPrice, array $batchOptional): array
    {
        $requestData = [
            'appid' => self::$appId,
            'mchid' => self::$mchId,
            'description' => $subject,
            'out_trade_no' => $orderNo,
            'notify_url' => self::$notifyPath,
            'amount' => [
                'total' => $orderPrice,
                'currency' => 'CNY',
            ],
            'payer' => [
                'openid' => $batchOptional['openid'],
            ],
            'attach' => $batchOptional['params'],
        ];

        $this->setBatchOptional($requestData, $batchOptional);

        // 请求参数
        LogHelper::info('payment', ['requestData' => $requestData]);

        // 发送请求
        $response = $this->getInstance()->chain(self::PAY_ORDER_JSAPI)->post(['json' => $requestData]);
        $result = json_decode($response->getBody()->getContents(), true);
        LogHelper::info('payment', ['response' => $response, 'result' => $result]);
        // 响应参数
        return $this->getAppletPayParams($result['prepay_id']);
    }

    /**
     * H5支付
     * @param string $subject 标题
     * * @param string $orderNo 订单号
     * * @param int $orderPrice 订单金额
     * * @param array $batchOptional 额外参数
     * @return array
     */
    public function jsApiPay(string $subject, string $orderNo, int $orderPrice, array $batchOptional): array
    {
//        $requestData = [
//            'appid' => self::$appId,
//            'mchid' => self::$mchId,
//            'description' => $subject,
//            'out_trade_no' => $orderNo,
//            'notify_url' => self::$notifyPath,
//            'amount' => [
//                'total' => $orderPrice,
//                'currency' => 'CNY',
//            ],
//            'payer' => [
//                'openid' => $batchOptional['openid'],
//            ]
//        ];
//
//        $this->setBatchOptional($requestData, $batchOptional);
//
//        // 请求参数
//        LogHelper::info('payment', ['requestData' => $requestData]);
//
//        // 发送请求
//        $response = $this->getInstance()->chain(self::PAY_ORDER_JSAPI)->post(['json' => $requestData]);
//        $result = json_decode($response->getBody()->getContents(), true);
//        LogHelper::info('payment', ['response' => $response, 'result' => $result]);
//        // 响应参数
//        return $this->getAppletPayParams($result['prepay_id']);
    }

    /**
     * 取消支付订单
     *
     * @param string $orderNo 订单号
     * @return void
     */
    public function cancel(string $orderNo)
    {
        try {
            $instance = $this->getInstance();

            // 请求参数
            LogHelper::info('payment', ['cancel-request' =>
                                            [
                                                'orderNo' => $orderNo,
                                                'mchid' => self::$mchId,
                                            ],
            ]);

            $requestData = [
                'mchid' => self::$mchId,
            ];
            $instance->chain(sprintf(self::PAY_ORDER_CANCEL, $orderNo))->post(['json' => $requestData]);
            LogHelper::info('payment', ['cancel-response' =>
                                            [
                                                'orderNo' => $orderNo,
                                                'mchid' => self::$mchId,
                                            ],
            ]);
        } catch (Exception $exception) {
            LogHelper::info('payment', ['exceptionCode' => $exception->getCode(), 'exceptionMessage' => $exception->getMessage()]);
        }
    }

    /**
     * 根据订单号查询支付订单
     *
     * @param string $orderNo 订单号
     * @return array
     * @throws Throwable
     */
    public function query(string $orderNo): array
    {
        try {
            $instance = $this->getInstance();
            LogHelper::info('payment', ['mchid' => self::$mchId, 'transactionId' => $orderNo]);

            $requestData = [
                'mchid' => self::$mchId,
            ];

            $response = $instance->chain(sprintf(self::PAY_TRANSACTION_QUERY, $orderNo))
                                 ->get(['query' => $requestData]);
            $result = json_decode($response->getBody()->getContents(), true);

            LogHelper::info('payment', ['response' => $response, 'result' => $result]);

            switch ($result['trade_state']) {
                case 'NOTPAY':
                    $status = self::QUERY_PAY_STATUS_WAIT; // 未付款
                    break;
                case 'SUCCESS':
                    $status = self::QUERY_PAY_STATUS_SUCCESS; // 支付成功
                    break;
                case 'CLOSED': // 已关闭
                    $status = self::QUERY_PAY_STATUS_CLOSED; // 已关闭
                    break;
                default:
                    $status = self::QUERY_PAY_STATUS_WAIT;
            }

            return [
                'orderNo' => $result['out_trade_no'] ?? '',
                'status' => $status,
                'totalAmount' => $result['amount']['total'] ?? 0,
                'transactionId' => $result['transaction_id'] ?? '',
                'payTime' => $result['success_time'] ?? '',
            ];
        } catch (Throwable $exception) {
            if (strpos($exception->getMessage(), 'ORDER_NOT_EXIST') !== false) {
                return [
                    'orderNo' => '',
                    'status' => self::QUERY_PAY_STATUS_NOT_FOUND, // 不存在
                    'totalAmount' => 0,
                    'transactionId' => '',
                    'payTime' => '',
                ];
            }

            throw $exception;
        }
    }

    /**
     * 解密
     *
     * @param $associatedData
     * @param $nonce
     * @param $ciphertext
     * @return string
     * @throws Exception
     */
    public function decrypt($params): array
    {
        // 使用PHP7的数据解构语法，从Array中解构并赋值变量
        ['resource' => [
            'ciphertext' => $ciphertext,
            'nonce' => $nonce,
            'associated_data' => $aad
        ]] = $params;
        // 加密文本消息解密
        $decrypted = AesGcm::decrypt($ciphertext, self::$mchSecretKey, $nonce, $aad);
        if ($decrypted == '') {
            LogHelper::info('callback', ['解密失败', 'decrypt' => $decrypted, 'params' => $params]);
            return [];
        }

        return json_decode($decrypted, true);
    }
}
